#ifdef WIN32
#include <Windows.h>
#endif // WIN32

#ifdef __linux__
#include <dlfcn.h>
#endif

#include <rxplugin.h>

struct RXPluginLoader::Private
{
	Private() {
        handle = NULL;
		interfacefun = NULL;
	}

	typedef RXPluginInterface*(*funtype)();

	funtype interfacefun;

#ifdef WIN32
	HINSTANCE handle;
#endif // WIN32

#ifdef __linux__
    void* handle;
#endif
};

RXPluginLoader::RXPluginLoader():_P(new Private)
{

}

RXPluginLoader::~RXPluginLoader()
{
	unLoad();
}

bool RXPluginLoader::load(const char* lib)
{
	if (isLoaded()) {
		std::cerr << "PluginLoader Is In Loaded State" << std::endl;
		return false;
	}

#if WIN32
	_P->handle = LoadLibrary(lib);
#endif // WIN32

#if __linux__
    _P->handle = dlopen(lib,RTLD_LAZY);
#endif

	if (!_P->handle)
	{
		return false;
	}
	
#if WIN32
    _P->interfacefun =  (Private::funtype)GetProcAddress(_P->handle, "LoadPlugin");
#endif

#if __linux__
    _P->interfacefun = (Private::funtype)dlsym(_P->handle,"LoadPlugin");
#endif

	if (!_P->interfacefun) {
#if WIN32
		FreeLibrary(_P->handle);
#endif
#if __linux__
        dlclose(_P->handle);
#endif
		_P->handle = NULL;
		std::cerr << "PluginLoader:Undefined reference to \"RXPluginInterface* LoadPlugin\"" << std::endl;
		return false;
	}

	return true;
}

void RXPluginLoader::unLoad()
{
	if (isLoaded()) {
#ifdef WIN32
		FreeLibrary(_P->handle);
#endif // WIN32
#if __linux__
        dlclose(_P->handle);
#endif
		_P->interfacefun = NULL;
	}
}

bool RXPluginLoader::isLoaded() const
{
	if (_P->handle) {
		return true;
	}
	return false;
}

std::vector<std::string> RXPluginInterface::ClassNames() const
{
	std::vector<std::string> ret;
	for (const auto& i : data.metas) {
		ret.push_back(i.first);
	}
	return ret;
}

RXPluginInterface * RXPluginLoader::GetPluginInterface()
{
	
	if (!isLoaded()) {
		return NULL;
	}

	if (!_P->interfacefun) {
		return NULL;
	}

	return _P->interfacefun();
}

RXPluginInterface::RXPluginInterface(const __RXPLUGIN_DATA & data)
{
	this->data = data;
}

MetaInfo RXPluginInterface::LoadClass(const char * className) const
{
	if (data.metas.count(className)) {
		return data.metas.at(className);
	}
	return MetaInfo();
}

std::string RXPluginInterface::GetName() const
{
	return data.pluginName.c_str();
}

std::string RXPluginInterface::GetAttribute(const char * name) const
{
	return data.attribute.at(name);
}

std::vector<std::string> RXPluginInterface::GetAttributeNames() const
{
	std::vector<std::string> ret;

	for (auto &i : data.attribute)
	{
		ret.push_back(i.first);
	}

	return ret;
}

std::string RXPluginInterface::GetInfomation() const
{
	return data.infomation;
}

RXPluginImplment::RXPluginImplment(const __RXPLUGIN_DATA & data):RXPluginInterface(data)
{
}

void RXPluginImplment::RegisterMetaInfo(const MetaInfo & meta)
{
	data.metas[meta.ClassName()] = meta;
}
